<?php

// 闭包初始化行为
Hook::add('action_begin', function () {
    // 获取系统配置
    //$ActionPlug = cache('sys.action_plug_cache');
	$ActionPlug=[];
    if (empty($ActionPlug) || $ActionPlug == []) {
		$ActionPlug = cache('sys.action_plug');
        // 初始化钩子
		$Plugs = [];
        foreach ($ActionPlug as $K => $V) {
			foreach ($V as $K2 => $V2) {
				$PK = $K.'__'.$V2['id'];
				$Plugs[$PK] = get_plug_class($V2['plug']);
				Hook::add($PK, $Plugs[$PK]);
			}
        }
		cache('sys.action_plug_cache',$Plugs);
    } else {
        Hook::import($ActionPlug, false);
    }
});

function LoaderParseName($name, $type = 0){
	if ($type) {
		return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) {
			return strtoupper($match[1]);
		}, $name));
	} else {
		return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
	}
}
/**
 * 获取插件类的类名
 * @param $name 插件名
 * @param string $type 返回命名空间类型
 * @param string $class 当前类名
 * @return string
 */
function get_plug_class($name, $type = 'plug', $class = null){
    // 处理多级控制器情况
    if (!is_null($class) && strpos($class, '.')) {
        $class = explode('.', $class);
        foreach ($class as $key => $cls) {
            $class[$key] = LoaderParseName($cls, 1);
        }
        $class = implode('\\', $class);
    } else {
        $class = LoaderParseName(is_null($class) ? $name : $class, 1);
    }
    switch ($type) {
        case 'controller':
            $namespace = "\\plug\\" . $name . "\\controller\\" . $class;
            break;
        default:
            $namespace = "\\plug\\" . $name . "\\" . $class;
    }
    return class_exists($namespace) ? $namespace : '';
}

/**
 * 获取插件类的配置文件数组
 * @param string $name 插件名
 * @return array
 */
function get_plug_config($name)
{
    $class = get_plug_class($name);
    if (class_exists($class)) {
        $plug = new $class();
        return $plug->getConfig();
    } else {
        return [];
    }
}

/**
 * 插件显示内容里生成访问插件的url
 * @param $url
 * @param array $param
 * @return bool|string
 * @param bool|string $suffix 生成的URL后缀
 * @param bool|string $domain 域名
 */
function plug_url($url, $param = [], $suffix = true, $domain = false)
{
    $url = parse_url($url);
    $case = config('url_convert');
    $plugs = $case ? LoaderParseName($url['scheme']) : $url['scheme'];
    $controller = $case ? LoaderParseName($url['host']) : $url['host'];
    $action = trim($case ? strtolower($url['path']) : $url['path'], '/');

    /* 解析URL带的参数 */
    if (isset($url['query'])) {
        parse_str($url['query'], $query);
        $param = array_merge($query, $param);
    }

    // 生成插件链接新规则
    $actions = "{$plugs}-{$controller}-{$action}";

    return url("plugs/execute/{$actions}", $param, $suffix, $domain);
}

// +----------------------------------------------------------------------
// | 用户相关操作
// +----------------------------------------------------------------------
/**
 * @name 用户是否登陆
 * @description 根据用户登录状态返回 用户ID 或者 false
 */
function _Is_Login() {
	// 获取 Auth的Key
	$_Uid = session ( config ( 'auth_key' ) );
	if ($_Uid) {
		return $_Uid;
	} else {
		return false;
	}
}


/**
 * @name 用户是否为超级管理员
 * @desc 根据用户ID与超管用户数组作对比返回
 */
function _IsAdmin($_Uid = false) {
	if (! $_Uid) {
		$_Uid = _Is_Login ();
	}
	if (in_array ( $_Uid, config ( 'auth_super_admin' ) )) {
		return true;
	}
	return false;
}

/**
 * @name 用户ID
 * @desc 返回 用户ID 或者 false
 */
function _Uid() {
	// 获取 Auth的Key
	$_Uid = _Is_Login();
	return $_Uid;
}

/**
 * @name 用户信息
 * @desc 根据id获取用户信息
 */
function _UInfo($_Uid = false) {
	if($_Uid){
		//获取指定ID的用户
		return model('app\common\logic\User')->_Uinfo($_Uid);
	}else{
		//获取当前登陆用户
		$_Uid = _Uid();
		if(!$_Uid){
			return false;
		}else{
			return session('uinfo');
		}
	}
}
// +----------------------------------------------------------------------
// | 权限相关
// +----------------------------------------------------------------------

/**
 * @name 是否有权限进行操作
 * @desc 根据用户id和当前路由信息 判断是否可以有权限
 */
function _CheckAuth($_Mca = false, $_Uid = false) {
	// 判断是否为超级管理 如果是 拥有所有权限
	if (_IsAdmin ( $_Uid )) {
		return true;
	}
	// 判断是否有Mca数据传递过来
	if (! $_Uid) {
		$_Uid = _Is_Login ();
	}
	// 判断是否有Mca数据传递过来
	if (! $_Mca) {
		$_Mca = _Mca (true);
	}
	$_Mca = _FormatMca($_Mca,true);
	// 如果Mca数据长度小于3则为不正常数据 返回错误
	if (count ( $_Mca ) < 3) {
		return false;
	}
	if (_AuthNotMca ( $_Mca )) {
		return true;
	}
	$_Mca = _FormatMca($_Mca,false);
	// 实例化Auth类
	$_Auth = new \sys\Auth ();
	if (! $_Auth->check ( $_Mca, $_Uid )) {
		return false;
	}
	return true;
}

/**
 * @name 无需验证的控制器方法
 * @desc 根据每个模块下配置文件中的auth_not_mca 进行判断
 */
function _AuthNotMca($_Mca = false,$_Data = []) {
	// 判断是否有Mca数据传递过来
	if (! $_Mca) {
		$_Mca = _Mca ();
	}
	$_Mca = _FormatMca($_Mca,true);
	// 判断当前的mca是否在非验证路由中
	if (in_array ( $_Mca ['m'] . '/' . $_Mca ['c'] . '/*', config ( 'auth_not_mca' ) )) {
		return true;
	}
	if (in_array ( $_Mca ['m'] . '/' . $_Mca ['c'] . '/' . $_Mca ['a'], config ( 'auth_not_mca' ) )) {
		return true;
	}
	return false;
}
/**
 * @name 格式化路由
 * @desc 返回字符串或者数组
 */
function _FormatMca($Mca,$RType = true) {
	if($RType){
		if(is_string ($Mca)){
			$Temp = explode ( "/", $Mca );
			$Return ['m'] = $Temp [0];
			$Return ['c'] = $Temp [1];
			$Return ['a'] = $Temp [2];
		}else{
			if(array_key_exists('m',$Mca) && array_key_exists('c',$Mca) && array_key_exists('a',$Mca)){
				$Return = $Mca;
			}else{
				$Return ['m'] = $Mca [0];
				$Return ['c'] = $Mca [1];
				$Return ['a'] = $Mca [2];
			}
		}
	}else{
		if(is_string ($Mca)){
			$Return = $Mca;
		}else{
			if(array_key_exists('m',$Mca) && array_key_exists('c',$Mca) && array_key_exists('a',$Mca)){
				$Return = $Mca ['m'] . '/' . $Mca ['c'] . '/' . $Mca ['a'];
			}else{
				$Return = $Mca [0] . '/' . $Mca [1] . '/' . $Mca [2];
			}
		}
	}
	return $Return;
}

/**
 * @name 获取当前路由
 * @desc 根据request 获取当前的 module controller action
 */
function _Mca($Type=false) {
	$Mca = [];
	$Request = request();
	$Mca ['m'] = $Request->module ();
	$Mca ['c'] = $Request->controller ();
	$Mca ['a'] = $Request->action ();
	$Mca = _FormatMca($Mca,!$Type);
	return $Mca;
}

/**
 * @name 路由信息转为小写
 * @desc 某段路由为多个大写字母会转为以'_'连接的小写
 * <{
 * @type call
 * @param string $_Mca(路由信息:模块/控制器/方法 如果未传递或者为false 则为当前路由)
 * @param string $_Split(转换后的连接符:默认为".")
 * @return string $_Res(转换后的路由信息)
 * @return boolean false(:传递数据不合法)
 * }>
 */
function _SplitMca($_Mca='',$_Split='.') {
	if(!is_string($_Mca) || !is_string($_Split)){
		return false;
	}
	if($_Mca==''){
		$_Mca = _Mca (true);
	}
	$_Arr = explode ( "/", $_Mca );
	foreach ( $_Arr as $_K => $_One ) {
		$_One = preg_split ( "/(?=[A-Z])/", $_One );
		foreach ( $_One as $_K2 => $_V ) {
			$_One [$_K2] = strtolower ( $_V );
			if (! $_V) {
				unset ( $_One [$_K2] );
			}
		}
		$_Arr [$_K] = implode ( '_', $_One );
	}
	$_Res = implode ( $_Split, $_Arr );
	return $_Res;
}

// +----------------------------------------------------------------------
// | 操作相关
// +----------------------------------------------------------------------

/**
 * @name ajax返回操作返回信息
 * <{
 * @type call
 * @param string $_Code(操作码:一般200为操作成功)
 * @param string $_Msg(消息:可以为空)
 * @param obj $_Data(返回数据:根据数据的类型自动转为对应的类型 可以为空)
 * @return string $_Res(json字符串)
 * }>
 */
function _Code($_Code = '200', $_Msg = '', $_Data = false) {
	$_Res = [ 
		'code' => $_Code,
		'msg' => $_Msg 
	];
	if ($_Data) {
		$_Res ['data'] = $_Data;
	}
	return json ( $_Res );
}

// +----------------------------------------------------------------------
// | Extjs相关
// +----------------------------------------------------------------------

/**
 * @name 排序
 * @desc 多种条件排序
 * <{
 * @type call
 * @param string $_Str(排序的字符串)
 * @return array $_Res(排序数组)
 * }>
 */
function _ExtSort($_Str = '') {
	$_Arr = json_decode ( $_Str, true );
	$_Res = [ ];
	foreach ( $_Arr as $_K => $_One ) {
		$_Res [$_One ['property']] = $_One ['direction'];
	}
	return $_Res;
}


/**
 * @name 搜索字段拼装
 * @desc 通过搜索数据,搜索字段,预设后缀进行 搜索字段的数据拼装
 */
function _SerchFieldAssembly($Parm = [], $Fields = [], $Pre = 's_') {
	// 查询条件
	$Return = [];
	// 清除空数组元素
	foreach ( $Parm as $K => $V ) {
		if ($V == '' || $V == null) {
			unset ( $Parm [$K] );
		}
	}
	foreach ( $Fields as $K => $V ) {
		$V = strtolower($V);
		if(array_key_exists($Pre.$K,$Parm)){
			switch ($V){
				case '=':
				case '<>':
				case '>':
				case '>=':
				case '<':
				case '<=':
				case 'between':
				case 'not_between':
				case 'in':
				case 'not_in':
					$Return[] = [$K,$V,$Parm[$Pre.$K]];
				break;
				case 'like':
					$Return[] = [$K,'like','%'.$Parm[$Pre.$K].'%'];
				break;
				case 'datatime':
					if(array_key_exists($Pre.$K.'_min',$Parm) && array_key_exists($Pre.$K.'_max',$Parm)){
						$Return[] = [$K,'between',[strtotime($Parm[$Pre.$K.'_min']),strtotime($Parm[$Pre.$K.'_max'])]];
					}
					if(!array_key_exists($Pre.$K.'_min',$Parm) && array_key_exists($Pre.$K.'_max',$Parm)){
						$Return[] = [$K,'<=',strtotime($Parm[$Pre.$K.'_max'])];
					}
					if(array_key_exists($Pre.$K.'_min',$Parm) && !array_key_exists($Pre.$K.'_max',$Parm)){
						$Return[] = [$K,'>=',strtotime($Parm[$Pre.$K.'_min'])];
					}
				break;
				default:;
			}
		}
	}
	//返回查询条件
	return $Return;
}

/**
 * @name 列表转extjs格式的树
 * @desc 主要为增加了 expanded 和 leaf 的判断
 * <{
 * @type call
 * @param arrat $_Data(数组)
 * @param string $pk(主键)
 * @param string $pid(父键)
 * @param string $child(子集的key)
 * @param string $root(根节点ID)
 * @param string $_Expand(:用于计数 填写0)
 * @param string $_Layer(默认展示几层)
 * @return array $_Tree(最后形成的树)
 * }>
 */
function _Ext_ListToTree($_Data, $pk = 'id', $pid = 'p_id', $child = 'children', $root = "",$_Expand=0,$_Layer = 0)
{
	$_Tree = [];
	$_Layer = $_Layer + 1;
	foreach($_Data as $k => $v)
	{
		if($v[$pid] == $root)
		{
			$v['leaf'] = false;
			/* || $_Expand === true*/
			if($_Expand >= $_Layer){
				$v['expanded'] = true;
			}
			$v[$child] = _Ext_ListToTree($_Data,$pk,$pid,$child,$v[$pk],$_Expand,$_Layer);
			if(!is_array($v[$child])){
				unset($v[$child]);
				$v['leaf'] = true;
			}
			$_Tree[] = $v;
		}
	}
	return $_Tree;
}

// +----------------------------------------------------------------------
// | Sql与数据库相关
// +----------------------------------------------------------------------

/**
 * @name 处理sql语句
 * @desc 处理sql语句，执行替换前缀都功能。
 * <{
 * @type call
 * @param string $sql(未处理的Sql)
 * @param string $tablepre(需要替换的前缀)
 * @return string $ret(处理后的sql语句)
 * }>
 */
function _SqlSplit($sql, $tablepre) {
	if ($tablepre != "tg_")
		$sql = str_replace("tg_", $tablepre, $sql);
	$sql = preg_replace("/TYPE=(InnoDB|MyISAM|MEMORY)( DEFAULT CHARSET=[^; ]+)?/", "ENGINE=\\1 DEFAULT CHARSET=utf8", $sql);
	if ($r_tablepre != $s_tablepre)
		$sql = str_replace($s_tablepre, $r_tablepre, $sql);
	$sql = str_replace("\r", "\n", $sql);
	$ret = array();
	$num = 0;
	$queriesarray = explode(";\n", trim($sql));
	unset($sql);
	foreach ($queriesarray as $query) {
		$ret[$num] = '';
		$queries = explode("\n", trim($query));
		$queries = array_filter($queries);
		foreach ($queries as $query) {
			$str1 = substr($query, 0, 1);
			if ($str1 != '#' && $str1 != '-')
				$ret[$num] .= $query;
		}
		$num++;
	}
	return $ret;
}

/**
 * @name 执行Sql
 * <{
 * @type call
 * @param string $_Sql(Sql语句)
 * @return boolean true/false(执行结果:成功返回true,失败返回false)
 * }>
 */
function _SqlRun($_Sql)
{
    try {
        db()->execute($_Sql);
    } catch (Exception $e) {
        return false;
    }
    return true;
}

/**
 * @name 执行Sql文件
 * <{
 * @type call
 * @param string $_SqlFile(Sql文件路径)
 * @param string $_Prefix(Sql需要替换的前缀)
 * @return boolean true/false(执行结果:成功返回true,失败返回false)
 * }>
 */
function _SqlFileRun($_SqlFile, $_Prefix)
{
    $_SqlFile = realpath($_SqlFile);
    if (file_exists($_SqlFile)) {
        if (isset($_Prefix) && !empty($_Prefix)) {
            $_Sql_Statement = \sys\Sql::getSqlFromFile($_SqlFile, false, [
                $_Prefix => config('database.prefix')
            ]);
        } else {
            $_Sql_Statement = \sys\Sql::getSqlFromFile($_SqlFile);
        }
        if (!empty($_Sql_Statement)) {
            foreach ($_Sql_Statement as $_Sql) {
				try {
                	_SqlRun($_Sql);
				} catch (Exception $e) {
					return false;
				}
            }
        }
    }
    return true;
}

// +----------------------------------------------------------------------
// | 字符串操作
// +----------------------------------------------------------------------

/**
 * @name 生成guid
 * @desc 根据多个因子生成字符串
 * <{
 * @type call
 * @return string $_Str(唯一guid)
 * }>
 */
function _Guid() {
	$_Str = md5 ( uniqid ( mt_rand (), true ).time() );
	return $_Str;
}
/**
 * @name 加密解密
 * @desc 根据多个因子加密解密
 * <{
 * @type call
 * @return Str $_Str(唯一guid)
 * }>
 */
function _AutoCode($Str, $Type = false, $Key = '', $Expiry = 3600) {
    // 动态密匙长度
    $CKeyLength = 4; 
    // 密匙   
    $Key = md5($Key ? $Key : config('_DecodeKey'));
    // 密匙a会参与加解密   
    $Keya = md5(substr($Key, 0, 16));   
    // 密匙b会用来做数据完整性验证   
    $Keyb = md5(substr($Key, 16, 16));   
    // 密匙c用于变化生成的密文   
    $Keyc = $CKeyLength ? ($Type ? substr($Str, 0, $CKeyLength): substr(md5(microtime()), -$CKeyLength)) : '';   
    // 参与运算的密匙   
    $CryptKey = $Keya.md5($Keya.$Keyc);   
    $KeyLength = strlen($CryptKey);   
    // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$Keyb(密匙b)， 
	//解密时会通过这个密匙验证数据完整性   
    // 如果是解码的话，会从第$CKeyLength位开始，因为密文前$CKeyLength位保存 动态密匙，以保证解密正确   
    $Str = $Type ? base64_decode(substr($Str, $CKeyLength)) :  sprintf('%010d', $Expiry ? $Expiry + time() : 0).substr(md5($Str.$Keyb), 0, 16).$Str;   
    $Str_length = strlen($Str);   
    $Result = '';   
    $Box = range(0, 255);   
    $RndKey = array();   
    // 产生密匙簿   
    for($i = 0; $i <= 255; $i++) {   
        $RndKey[$i] = ord($CryptKey[$i % $KeyLength]);   
    }   
    // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度   
    for($j = $i = 0; $i < 256; $i++) {   
        $j = ($j + $Box[$i] + $RndKey[$i]) % 256;   
        $Temp = $Box[$i];   
        $Box[$i] = $Box[$j];   
        $Box[$j] = $Temp;   
    }   
    // 核心加解密部分   
    for($a = $j = $i = 0; $i < $Str_length; $i++) {   
        $a = ($a + 1) % 256;   
        $j = ($j + $Box[$a]) % 256;   
        $Temp = $Box[$a];   
        $Box[$a] = $Box[$j];   
        $Box[$j] = $Temp;   
        // 从密匙簿得出密匙进行异或，再转成字符   
        $Result .= chr(ord($Str[$i]) ^ ($Box[($Box[$a] + $Box[$j]) % 256]));   
    }   
    if($Type) {  
        // 验证数据有效性，请看未加密明文的格式   
        if((substr($Result, 0, 10) == 0 || substr($Result, 0, 10) - time() > 0) &&  substr($Result, 10, 16) == substr(md5(substr($Result, 26).$Keyb), 0, 16)) {   
            return substr($Result, 26);   
        } else {   
            return '';   
        }   
    } else {   
        // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因   
        // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码   
        return $Keyc.str_replace('=', '', base64_encode($Result));   
    }   
}
function _AutoCodeUrl($Str, $Type = false, $Key = '', $Expiry = 3600) {
	if($Type){
		return _AutoCode(urldecode($Str), $Type, $Key, $Expiry);
	}else{
		return urlencode(_AutoCode($Str, $Type, $Key, $Expiry));
	}
}
/**
 * @name 字符串转换为数组
 * @desc 主要把explode函数的分隔符调整到第二个参数
 * <{
 * @type call
 * @param string $_Str(要分割的字符串)
 * @param string $_Glue(分割符)
 * @return string $_Str(执行结果)
 * }>
 */
function _StrToArr($_Str, $_Glue = ',')
{
    return explode( $_Glue,$_Str);
}

/**
 * @name 数组转换为字符串
 * @desc 主要把implode函数的分隔符调整到第二个参数
 * <{
 * @type call
 * @param string $_Str(要连接的数组)
 * @param string $_Glue(分割符)
 * @return string $_Str(执行结果)
 * }>
 */
function _Arr_To_Str($_Arr, $_Glue = ',')
{
    return implode($_Glue, $_Arr);
}
/**
 * 字符串拆分为属性
 * @author 管侯杰 <912524639@qq.com>
 */
function _StrToAttr($_Str, $_Glue_1 = "\r\n", $_Glue_2 = ':')
{
    $_Arr = [];
    $_Arr_1 = [];
    $_Arr_1 = _StrToArr($_Str, $_Glue_1);
    if (count($_Arr_1) > 0) {
        foreach ($_Arr_1 as $_One) {
            $_Arr_2 = [];
            $_Arr_2 = _StrToArr($_One, $_Glue_2);
            if (count($_Arr_2) > 0) {
                $_Arr[$_Arr_2[0]] = $_Arr_2[1];
            }
        }
    }
    return $_Arr;
}
/**
 * 字符串截取，支持中文和其他编码
 * @param string $_Str 需要转换的字符串
 * @param string $_Start 开始位置
 * @param string $_Len 截取长度
 * @param string $_Charset 编码格式
 * @param string $_Suffix 截断显示字符
 * @return string
 * @author 管侯杰 <912524639@qq.com>
 */
function _Msubstr($_Str, $_Start = 0, $_Len, $_Charset = "utf-8", $_Suffix = true)
{
    if (function_exists("mb_substr"))
        $_Slice = mb_substr($_Str, $_Start, $_Len, $_Charset);
    elseif (function_exists('iconv_substr')) {
        $_Slice = iconv_substr($_Str, $_Start, $_Len, $_Charset);
        if (false === $_Slice) {
            $_Slice = '';
        }
    } else {
        $_Re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $_Re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $_Re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $_Re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($_Re[$_Charset], $_Str, $_Match);
        $_Slice = join("", array_slice($_Match[0], $_Start, $_Len));
    }
    if ($_Suffix === false) {
        return $_Slice;
    } elseif ($_Suffix === true) {
        return $_Slice . '...';
    } else {
        return $_Slice . $_Suffix;
    }
}

/**
 * 清洗所有HTML标签
 * @param string text 文本内容
 * @return string 处理后内容
 */
function _Remove_Html($_Text, $_Adds = false)
{
    $_Text = nl2br($_Text);
    $_Text = strip_tags($_Text);
    if ($_Adds)
        $_Text = addslashes($_Text);
    $_Adds = trim($_Adds);
    return $_Adds;
}

/**
 * h函数用于过滤不安全的html标签，输出安全的html
 * @param string $text 待过滤的字符串
 * @param string $type 保留的标签格式
 * @return string 处理后内容
 */
function _Remove_Html_Tags($_Text, $_Type = 'html')
{
    // 无标签格式
    $text_tags = '';
    //只保留链接
    $link_tags = '<a>';
    //只保留图片
    $image_tags = '<img>';
    //只存在字体样式
    $font_tags = '<i><b><u><s><em><strong><font><big><small><sup><sub><bdo><h1><h2><h3><h4><h5><h6>';
    //标题摘要基本格式
    $base_tags = $font_tags . '<p><br><hr><a><img><map><area><pre><code><q><blockquote><acronym><cite><ins><del><center><strike>';
    //兼容Form格式
    $form_tags = $base_tags . '<form><input><textarea><button><select><optgroup><option><label><fieldset><legend>';
    //内容等允许HTML的格式
    $html_tags = $base_tags . '<ul><ol><li><dl><dd><dt><table><caption><td><th><tr><thead><tbody><tfoot><col><colgroup><div><span><object><embed><param>';
    //专题等全HTML格式
    $all_tags = $form_tags . $html_tags . '<!DOCTYPE><meta><html><head><title><body><base><basefont><script><noscript><applet><object><param><style><frame><frameset><noframes><iframe>';
    //过滤标签
    $_Text = strip_tags($_Text, ${$_Type . '_tags'});
    // 过滤攻击代码
    if ($_Type != 'all') {
        // 过滤危险的属性，如：过滤on事件lang js
        while (preg_match('/(<[^><]+)(ondblclick|onclick|onload|onerror|unload|onmouseover|onmouseup|onmouseout|onmousedown|onkeydown|onkeypress|onkeyup|onblur|onchange|onfocus|action|background[^-]|codebase|dynsrc|lowsrc)([^><]*)/i', $_Text, $_Mat)) {
            $_Text = str_ireplace($_Mat[0], $_Mat[1] . $_Mat[3], $_Text);
        }
        while (preg_match('/(<[^><]+)(window\.|javascript:|js:|about:|file:|document\.|vbs:|cookie)([^><]*)/i', $_Text, $_Mat)) {
            $_Text = str_ireplace($_Mat[0], $_Mat[1] . $_Mat[3], $_Text);
        }
    }
    return $_Text;
}
/**
 * $array1,'F',SORT_ASC,'F',SORT_DESC;
 */
function _Arr_Sorts(){
	$_Data = func_get_args();
	if(empty($_Data)){
		return null;
	}
	$_One = array_shift($_Data);
	foreach($_Data as $K => $V){
		if(is_string($V)){
			$_TEMP = array();
			foreach($_One as $K2=> $V2){
				$_TEMP[$K2] = $V2[$V];
			}
			$_Data[$K] = $_TEMP;
		}
	}
	$_Data[] = &$_One;//引用值
	call_user_func_array('array_multisort',$_Data);
	return array_pop($_Data);
}
/**
 * 二维数组排序
 * @param array $_Arr 查询结果
 * @param string $_Field 排序的字段名
 * @param array $_Sortby 排序类型(asc正向排序 desc逆向排序 nat自然排序)
 * @return array
 * @author 管侯杰 <912524639@qq.com>
 */
function _Arr_Sort($_Arr, $_Field, $_Sortby = 'asc')
{
    if (is_array($_Arr)) {
        $_Refer = $_Result_Set = [];
        foreach ($_Arr as $i => $_Data)
            $_Refer[$i] = &$_Data[$_Field];
        switch ($_Sortby) {
            case 'asc': // 正向排序
                asort($_Refer);
                break;
            case 'desc': // 逆向排序
                arsort($_Refer);
                break;
            case 'nat': // 自然排序
                natcasesort($_Refer);
                break;
        }
        foreach ($_Refer as $k => $_Val)
            $_Result_Set[] = &$_Arr[$k];
        return $_Result_Set;
    }
    return false;
}

/**
 * 列表转树
 * @param array $_List 要转换的数据集
 * @param string $_Pid parent标记字段
 * @param string $_Level level标记字段
 * @return array
 * @author 管侯杰 <912524639@qq.com>
 */
function _List_To_Tree($_List, $_Pk = 'id', $_Pid = 'pid', $_Child = '_child', $_Root = 0)
{
    $_Tree = [];
    if (is_array($_List)) {
        $_Refer = [];
        foreach ($_List as $k => $_Data) {
            $_Refer[$_Data[$_Pk]] =& $_List[$k];
        }
        foreach ($_List as $k => $_Data) {
            $_Parent_Id = $_Data[$_Pid];
            if ($_Root == $_Parent_Id) {
                $_Tree[] =& $_List[$k];
            } else {
                if (isset($_Refer[$_Parent_Id])) {
                    $_Parent =& $_Refer[$_Parent_Id];
                    $_Parent[$_Child][] =& $_List[$k];
					if(isset($_Parent['leaf'])){
						$_Parent['leaf'] = false;
					}
                }
            }
        }
    }
    return $_Tree;
}
function getTreeParented($Arr,$PK,$Res=[],$RootId=0,$PKName = 'id',$PidName = 'p_id')  {  
	foreach($Arr as $K => $V) {
		if($V[$PKName] == $PK) {
			if($V[$PidName] != $RootId){
				$Res = getTreeParented($Arr,$V[$PidName],$Res,$RootId,$PKName,$PidName);  
			}
			$Res[] = $V;
			break;
		}  
	}
	return $Res;
}  

function getTreeParentedField($Arr,$PK,$Glue='',$Field='name',$RootId=0,$PKName = 'id',$PidName = 'p_id')  {  
	$Res='';
	foreach($Arr as $K => $V) {
		if(!isset($V[$Field])){
			break;
		}
		if($V[$PKName] == $PK) {
			if($V[$PidName] != $RootId){
				$Res = $Res. getTreeParentedField($Arr,$V[$PidName],$Glue,$Field,$RootId,$PKName,$PidName).$Glue;  
			}
			$Res = $Res.$V[$Field];
			break;
		}  
	}
	return $Res;
}  
/**
 * 将_List_To_Tree的树还原成列表
 * @param  array $_Tree 原来的树
 * @param  string $_Child 孩子节点的键
 * @param  string $_Order 排序显示的键，一般是主键 升序排列
 * @param  array $_List 过渡用的中间数组，
 * @return array        返回排过序的列表数组
 * @author 管侯杰 <912524639@qq.com>
 */
function _Tree_To_List($_Tree, $_Child = '_child', $_Order = 'id', &$_List = [])
{
    if (is_array($_Tree)) {
        $_Refer = [];
        foreach ($_Tree as $K => $_Val) {
            $_Refer = $Val;
            if (isset($_Refer[$_Child])) {
                unset($_Refer[$_Child]);
                _Tree_To_List($_Val[$_Child], $_Child, $_Order, $_List);
            }
            $_List[] = $_Refer;
        }
        $_List = _Arr_Sort($_List, $_Order, $_Sortby = 'asc');
    }
    return $_List;
}

/**
 * 将数组中的某些字段组成新的数组返回
 * @author 管侯杰 <912524639@qq.com>
 */
function _Get_Arr_Field($_Arr, $_Fields)
{
    $_Result = [];
    foreach ($_Fields as $_One) {
        if (array_key_exists($_One, $_Arr)) {
            $_Result[$_One] = $array[$_One];
        }
    }
    return $_Result;
}

/**
 * 二维数组中的某些字段组成新的数组
 * @author 管侯杰 <912524639@qq.com>
 */
function _Get_Arrs_Field($_Arrs, $_Fields)
{
    foreach ($_Arrs as $_Key => $_One) {
        $_Arrs[$_Key] = _Get_Arr_Field($_One, $_Fields);
    }
    return $_Arrs;
}

/**
 * 字节格式化
 * @param  number $_Size 字节数
 * @param  string $_Delimiter 数字和单位分隔符
 * @return string            格式化后的带单位的大小
 * @author 管侯杰 <912524639@qq.com>
 */
function _Bytes_Format($_Size, $_Delimiter = '')
{
    $_Units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
    for ($i = 0; $_Size >= 1024 && $i < 5; $i++) $_Size /= 1024;
    return round($_Size, 2) . $_Delimiter . $_Units[$i];
}

/**
 * 时间戳格式化
 * @param int $time
 * @return string 完整的时间显示
 * @author 管侯杰 <912524639@qq.com>
 */
function _Time_Format($_Time = NULL, $_Format = 'Y-m-d H:i')
{
    $_Time = $_Time === NULL ? NOW_TIME : intval($_Time);
    return date($_Format, $_Time);
}

/**
 * 设置跳转页面URL
 * @author 管侯杰 <912524639@qq.com>
 */
function _Set_Redirect_Url($_Url)
{
    cookie('redirect_url', $_Url);
}

/**
 * 获取跳转页面URL
 * @author 管侯杰 <912524639@qq.com>
 */
function _Get_Redirect_Url()
{
    $_Url = cookie('redirect_url');
    return empty($_Url) ? WEB_PATH : $_Url;
}

/**
 * _Get_Rand随机生成一个字符串
 * @param int $length 字符串的长度
 * @param string $type 类型
 * @return string
 * @author 管侯杰 <912524639@qq.com>
 */
function _Get_Rand($_Len = 8, $_Type = 'all')
{
    $_Num = '0123456789';
    $_Letter = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    if ($_Type == 'num') {
        $_Chars = $_Num;
    } elseif ($_Type == 'letter') {
        $_Chars = $_Letter;
    } else {
        $_Chars = $_Letter . $_Num;
    }

    $_Str = '';
    for ($i = 0; $i < $_Len; $i++) {
        $_Str .= $_Chars[mt_rand(0, strlen($_Chars) - 1)];
    }
    return $_Str;

}

/**
 * 判断是否为Ie浏览器
 * @author 管侯杰 <912524639@qq.com>
 */
function _Is_Ie()
{
    $userAgent = $_SERVER['HTTP_USER_AGENT'];
    $pos = strpos($userAgent, ' MSIE ');
    if ($pos === false) {
        return false;
    } else {
        return true;
    }
}

/**
 * 判断是否为手机端
 * @author 管侯杰 <912524639@qq.com>
 */
function _Is_Mobile()
{
    $user_agent = $_SERVER['HTTP_USER_AGENT'];
    $mobile_agents = Array("240x320", "acer", "acoon", "acs-", "abacho", "ahong", "airness", "alcatel", "amoi", "android", "anywhereyougo.com", "applewebkit/525", "applewebkit/532", "asus", "audio", "au-mic", "avantogo", "becker", "benq", "bilbo", "bird", "blackberry", "blazer", "bleu", "cdm-", "compal", "coolpad", "danger", "dbtel", "dopod", "elaine", "eric", "etouch", "fly ", "fly_", "fly-", "go.web", "goodaccess", "gradiente", "grundig", "haier", "hedy", "hitachi", "htc", "huawei", "hutchison", "inno", "ipad", "ipaq", "ipod", "jbrowser", "kddi", "kgt", "kwc", "lenovo", "lg ", "lg2", "lg3", "lg4", "lg5", "lg7", "lg8", "lg9", "lg-", "lge-", "lge9", "longcos", "maemo", "mercator", "meridian", "micromax", "midp", "mini", "mitsu", "mmm", "mmp", "mobi", "mot-", "moto", "nec-", "netfront", "newgen", "nexian", "nf-browser", "nintendo", "nitro", "nokia", "nook", "novarra", "obigo", "palm", "panasonic", "pantech", "philips", "phone", "pg-", "playstation", "pocket", "pt-", "qc-", "qtek", "rover", "sagem", "sama", "samu", "sanyo", "samsung", "sch-", "scooter", "sec-", "sendo", "sgh-", "sharp", "siemens", "sie-", "softbank", "sony", "spice", "sprint", "spv", "symbian", "tablet", "talkabout", "tcl-", "teleca", "telit", "tianyu", "tim-", "toshiba", "tsm", "up.browser", "utec", "utstar", "verykool", "virgin", "vk-", "voda", "voxtel", "vx", "wap", "wellco", "wig browser", "wii", "windows ce", "wireless", "xda", "xde", "zte");
    $is_mobile = false;
    foreach ($mobile_agents as $device) {
        if (stristr($user_agent, $device)) {
            $is_mobile = true;
            break;
        }
    }
    return $is_mobile;
}


/**
 * 除共有部分
 * @author 管侯杰 <912524639@qq.com>
 */
function _Arr_Subtract($_Arr_1, $_Arr_2)
{
    return array_diff($_Arr_1, array_intersect($_Arr_1, $_Arr_2));
}

/**
 * _Get_Ip_Lookup  获取ip地址所在的区域
 * @param null $_Ip
 * @return bool|mixed
 * @author 管侯杰 <912524639@qq.com>
 */
function _Get_Ip_Lookup($_Ip = null)
{
    if (empty($_Ip)) {
        $_Ip = get_client_ip(0);
    }
    $_Res = @file_get_contents('http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js&ip=' . $_Ip);
    if (empty($_Res)) {
        return false;
    }
    $_JsonMatches = [];
    preg_match('#\{.+?\}#', $_Res, $_JsonMatches);
    if (!isset($_JsonMatches[0])) {
        return false;
    }
    $_Json = json_decode($_JsonMatches[0], true);
    if (isset($_Json['ret']) && $_Json['ret'] == 1) {
        $_Json['ip'] = $_Ip;
        unset($_Json['ret']);
    } else {
        return false;
    }
    return $_Json;
}


//验证文件夹路径是否存在，如果不存在生成，失败返回flase
function by_dir($_Dir_Path)
{
    if (!is_dir($_Dir_Path)) {
        $_Res = mkdir($_Dir_Path, 0777, true);
        if (!$_Res) {
            return false;
        } else {
            return true;
        }
    }
    return true;
}


function _Get_ArrShift($_ParamData,$_Param,$K=0){
	if(!$_ParamData){
		return false;
	}
	if (!array_key_exists($_Param[$K],$_ParamData)) {
		return false;
	}
	$_ParamData = $_ParamData[$_Param[$K]];
	if(count($_Param) == $K+1){
		return $_ParamData;
	}else{
		return _Get_ArrShift($_ParamData,$_Param,$K+1);
	}
	
}
//用户行为相关
/**
 * 记录行为日志，并执行该行为的规则
 * @param string $_Action 行为标识
 * @param array $_Res 参数
 */
function _Action($_Action = null, $_Res=[]){
    //参数检查
    if(empty($_Action)){
        return '行为标识不能为空';
    }
	//获取缓存中的用户行为列表
	$_SysAction = model('app\common\logic\Action')->_Cache(true);
	if(!array_key_exists($_Action,$_SysAction)){
		return ;
	}
	$_ActionInfo = $_SysAction[$_Action];
	if(isset($_Res['user_id'])){
		$_UserId = $_Res['user_id'];
	}else{
		$_UserId = _Is_Login();
		if(!$_UserId){
			$_UserId = 0;
		}
	}
	if(!isset($_Res['table'])){
		$_Res['table'] = '';
	}
	if(!isset($_Res['record_id'])){
		 $_Res['record_id'] = 0;
	}
    //行为日志 数据
    $_Data['action_id']	= $_ActionInfo['id'];
    $_Data['user_id']	= $_UserId;
    $_Data['action_ip']	= request()->ip();
    $_Data['table']		= $_Res['table'];
    $_Data['record_id']	= $_Res['record_id'];
    $_Data['time']	= date('Y-m-d H:i:s',time());
	//判断是否有日志数据传入
	if(isset($_Res['remark'])){
    	$_Data['remark']	= $_Res['remark'];
	}else{
		//解析日志规则,生成日志备注
		if(!empty($_ActionInfo['log'])){
			if(preg_match_all('/\[(\S+?)\]/', $_ActionInfo['log'], $_Match)){
				//获取当前用户数据
				$_ParamData['Uinfo'] = _UInfo($_UserId);
				$_ParamData['Data'] = $_Data;
				$_ParamData['Res'] = $_Res;
				foreach ($_Match[1] as $_Val){
					$_Str = '';
					$_Param = explode('.', $_Val);
					if(count($_Param)>1){
						$_Str = _Get_ArrShift($_ParamData,$_Param);
						if($_Str){
							$_Replace[] = $_Str;
						}else{
							trace('[ThinkGms] 行为 - 日志规则解析 - '.$_Action,'error');
							$_Replace[] = '';
						}
					}else{
						$_Replace[] = $_Param[0];
					}
				}
				$_Data['remark'] =   str_replace($_Match[0], $_Replace, $_ActionInfo['log']);
			}else{
				$_Data['remark'] =   $_ActionInfo['log'];
			}
		}else{
			//未定义日志规则，记录操作url
			$data['remark']     =   '操作时间：'.$_Data['time'].'; 操作路径：'.$_SERVER['REQUEST_URI'];
		}
	}
    model('app\admin\model\ActionLog')->allowField ( true )->isUpdate ( false )->save ( $_Data );
	$ActionPlug = cache('sys.action_plug');
	if(array_key_exists($_Action,$ActionPlug)){
		foreach($ActionPlug[$_Action] as $K => $V){
			Hook::listen($_Action.'__'.$V['id'],[$V,$_Res]);
		}
	}
}
